﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Caching;
using System.Timers;
using TeamCityMonitor.Models;
using TeamCitySharp.DomainEntities;

namespace TeamCityMonitor.Repository.Caches
{
    public class TeamCityCache : IBuildRepository
    {
        private static Timer refreshStatusCacheTimer;
        private static Timer refreshSlowCacheTimer;
        private static ObjectCache cache;

        static TeamCityCache()
        {
            cache = MemoryCache.Default;
            refreshSlowCacheTimer = new Timer(2000); //Initial value to ensure data as fast as possible
            refreshSlowCacheTimer.Elapsed += RefreshSlowCacheTimerOnElapsed;
            refreshSlowCacheTimer.Start();

            refreshStatusCacheTimer = new Timer(Properties.Settings.Default.RefreshQuickCacheInterval);
            refreshStatusCacheTimer.Elapsed += RefreshStatusCacheTimerOnElapsed;
            refreshStatusCacheTimer.Start();

            cache["teamcity_quickdata"] = new TeamCityQuickDataCacheObject();
            cache["teamcity_slowdata"] = new TeamCitySlowDataCacheObject();
        }

        private static void RefreshStatusCacheTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
        {
            refreshStatusCacheTimer.Stop();
            try
            {
                var repo = new BuildRepository(new UserPictureRepository());
                
                var expectedTroubledBuildConfigIds = (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).TroubledProjects.Select(project => project.BuildConfig).ToList();
                var currentTroubledBuildIds = expectedTroubledBuildConfigIds.Select(id => repo.GetLastestBuildByBuildConfigId(id))
                                                .Where(build => build.Status != "SUCCESS")
                                                    .Select(build => build.BuildTypeId).ToList();
                (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).TroubledProjects
                            .RemoveAll(build => currentTroubledBuildIds.Contains(build.BuildConfig) == false);


                (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).TroubledProjects
                                                .ForEach(project => project.IsRunning = false);
                var runningBuildConfigIds = repo.GetRunningBuildConfigIds();
                (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).TroubledProjects.Where(project => runningBuildConfigIds.Contains(project.BuildConfig))
                    .ToList().ForEach(project => project.IsRunning = true);
                cache["teamcity_quickdata"] = new TeamCityQuickDataCacheObject {RunningBuildIds = runningBuildConfigIds};

            }
            catch (Exception e)
            {
                log4net.LogManager.GetLogger("Main").Error("RefreshStatusCacheTimerOnElapsed", e);
            }
            refreshStatusCacheTimer.Interval = Properties.Settings.Default.RefreshQuickCacheInterval;
            refreshStatusCacheTimer.Start();
        }

        private static void RefreshSlowCacheTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
        {
            refreshSlowCacheTimer.Stop();
            try
            {
                var repo = new BuildRepository(new UserPictureRepository());

                var troubledProjects = repo.GetAllTroubledProjectModels();
                var buildConfigurations = repo.GetBuildConfigurations().ToList();
                var users = repo.GetUsers().ToList();
                var noOfConfigs = repo.GetNoOfConfigurations();
                var noOfErrorBuilds = repo.GetNoOfErrorBuildsSince(Properties.Settings.Default.DaysBackToPerformStatistisc);
                var noOfFailingBuilds = repo.GetNoOfFailingBuildsSince(Properties.Settings.Default.DaysBackToPerformStatistisc);
                var noOfSuccesfullBuilds = repo.GetNoOfSuccessfullBuildsSince(Properties.Settings.Default.DaysBackToPerformStatistisc);
                var noOfProjects = repo.GetNoOfProjects();

                var noOfBuildsByUser = new Dictionary<string, int>();
                users.ForEach(user => noOfBuildsByUser.Add(user.Id, repo.GetNoOfBuildsByUser(user)));

                var noOfErrorsByConfig = new Dictionary<string, int>();
                buildConfigurations.ForEach(config => noOfErrorsByConfig.Add(config.Id, repo.GetNoOfErrorBuildsByBuildConfig(config)));

                var noOfFailingByConfig = new Dictionary<string, int>();
                buildConfigurations.ForEach(config => noOfFailingByConfig.Add(config.Id, repo.GetNoOfFailingBuildsByBuildConfig(config)));

                var noOfSuccessfullByConfig = new Dictionary<string, int>();
                buildConfigurations.ForEach(config => noOfSuccessfullByConfig.Add(config.Id, repo.GetNoOfSuccessfullBuildsByBuildConfig(config)));

                var serverInfo = repo.GetServerInfo();

                var cacheObject = new TeamCitySlowDataCacheObject()
                                            {
                                                BuildConfigurations = buildConfigurations,
                                                NoOfBuildsByUser = noOfBuildsByUser,
                                                NoOfConfigurations = noOfConfigs,
                                                NoOfErrorBuildsByBuildConfig = noOfErrorsByConfig,
                                                NoOfErrorBuildsSince = noOfErrorBuilds,
                                                NoOfFailingBuildsByBuildConfig = noOfFailingByConfig,
                                                NoOfFailingBuildsSince = noOfFailingBuilds,
                                                NoOfProjects = noOfProjects,
                                                NoOfSuccessfullBuildsByBuildConfig = noOfSuccessfullByConfig,
                                                NoOfSuccessfullBuildsSince = noOfSuccesfullBuilds,
                                                TroubledProjects = troubledProjects,
                                                Users = users,
                                                ServerInfo = serverInfo
                                            };

                cache["teamcity_slowdata"] = cacheObject;
            }
            catch (Exception e)
            {
                log4net.LogManager.GetLogger("Main").Error("RefreshSlowCacheTimerOnElapsed", e);
            }

            refreshSlowCacheTimer.Interval = Properties.Settings.Default.RefreshSlowCacheInterval;
            refreshSlowCacheTimer.Start();
        }

        public List<ProjectModel> GetAllTroubledProjectModels()
        {
            return (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).TroubledProjects;
        }

        public IEnumerable<User> GetUsers()
        {
            return (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).Users;
        }

        public List<string> GetRunningBuildConfigIds()
        {
            return (cache["teamcity_quickdata"] as TeamCityQuickDataCacheObject).RunningBuildIds;
        }

        public int GetNoOfProjects()
        {
            return (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).NoOfProjects;
        }

        public int GetNoOfConfigurations()
        {
            return (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).NoOfConfigurations;
        }

        public int GetNoOfSuccessfullBuildsSince(int daysBack)
        {
            return (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).NoOfSuccessfullBuildsSince;
        }

        public int GetNoOfFailingBuildsSince(int daysBack)
        {
            return (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).NoOfFailingBuildsSince;
        }

        public int GetNoOfErrorBuildsSince(int daysBack)
        {
            return (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).NoOfErrorBuildsSince;
        }

        public int GetNoOfBuildsByUser(User user)
        {
            var dict = (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).NoOfBuildsByUser;
            return dict.ContainsKey(user.Id) ? dict[user.Id] : 0;
        }

        public IEnumerable<BuildConfig> GetBuildConfigurations()
        {
            return (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).BuildConfigurations;
        }

        public int GetNoOfSuccessfullBuildsByBuildConfig(BuildConfig config)
        {
            var dict = (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).NoOfSuccessfullBuildsByBuildConfig;
            return dict.ContainsKey(config.Id) ? dict[config.Id] : 0;
        }

        public int GetNoOfFailingBuildsByBuildConfig(BuildConfig config)
        {
            var dict = (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).NoOfFailingBuildsByBuildConfig;
            return dict.ContainsKey(config.Id) ? dict[config.Id] : 0;
        }

        public int GetNoOfErrorBuildsByBuildConfig(BuildConfig config)
        {
            var dict = (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).NoOfErrorBuildsByBuildConfig;
            return dict.ContainsKey(config.Id) ? dict[config.Id] : 0;
        }

        public Build GetLastestBuildByBuildConfigId(string buildConfigId)
        {
            throw new NotImplementedException();
        }

        public Server GetServerInfo()
        {
            return (cache["teamcity_slowdata"] as TeamCitySlowDataCacheObject).ServerInfo;
        }
    }
}